package com.jkkc.lib.base.util.logcat

import android.text.TextUtils
import com.blankj.utilcode.util.ThreadUtils
import com.elvishew.xlog.XLog
import com.jkkc.fastface.util.logcat.Shell
import com.jkkc.lib.base.constant.Constant
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

/**
 * 通过系统shell调用logcat命令记录logcat日志到文件中
 * 用于通过logcat日志定位问题
 * 流程： 通过shell命令发起logcat实时记录日志进程并返回进程id，通过进程id结束日志记录。
 * @author chenxiaojin
 * @date 2022/12/08
 *
 */
object LogcatManager {
    // 过滤日志时间格式
    private val filterLogDateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)

    // 日志文件时间格式
    private val logDateFormat = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)
    private val logcatPidList = mutableListOf<String>()

    // logcat日志目录的总文件个数，超过这个个数会删除目录
    private const val RECORD_FILE_MAX_COUNT = 1000
    // efs.util.http

    // 每个日志文件大小, 单位：KB
    private const val LOG_FILE_SIZE = 5 * 1024

    // 每次记录日志文件个数, 超过这个数量则一直循环覆盖记录
    private const val LOG_FILE_COUNT = 200

    // 日志过滤，当网络不佳时，efs包会打印大量日志，影响定位，因此过滤掉
    //'^(?!.*(AAA|BBB)).*$
    // 2022-12-09 15:37:11.121 21004-21038/com.jkkc.fasttest E/efs.util.http: get request 'https://errlog.umeng.com/apm_cc' error， maybe network is disconnect
    private const val LOG_FILTER = "'^(?!.*(errlog.umeng.com)).*'"

    /**
     * 将所有logcat日志写入指定文件路径并开始实时记录
     * @param filePath 日志文件路径
     */
    fun startRecordLogcat(filePath: String, callback: (logcatPid: String?) -> Unit) {
        startRecordLogcat(filePath, null, LOG_FILE_SIZE, LOG_FILE_COUNT, callback)
    }

    /**
     * 从当前时间开始实时记录logcat日志到指定文件
     * @param filePath 日志文件路径
     */
    @JvmStatic
    @JvmOverloads
    fun startRecordLogcatRightNow(filePath: String, callback: ((logcatPid: String?) -> Unit)? = null) {
        startRecordLogcat(
            filePath,
            filterLogDateFormat.format(Date()),
            LOG_FILE_SIZE,
            LOG_FILE_COUNT,
        ) {
            callback?.invoke(it)
        }
    }

    /**
     * 开始记录整个APP的Logcat日志
     * 每个日志文件5m
     * 一共10个文件， 共50mb
     */
    @JvmStatic
    @JvmOverloads
    fun startRecordAPPLogcat(filePath: String, callback: ((logcatPid: String?) -> Unit)? = null) {
        startRecordLogcat(filePath, null, 5120, 10) {
            callback?.invoke(it)
        }
    }

    /**
     * 结束某个logcat日志记录进程
     * @param logcatPid 日志记录进程id
     */
    fun stopRecordLogcat(logcatPid: String?) {
        if (TextUtils.isEmpty(logcatPid)) {
            return
        }
        ThreadUtils.getCachedPool().execute {
            val shell = Shell("sh")
            try {
                // 杀掉指定进程
                val command = "kill -9 $logcatPid"
                val result = shell.run(command)
                if (result.isSuccess) {
                    XLog.i("Stop record logcat success, pid:$logcatPid")
                    // 从记录日志进程id列表移除已杀掉的进程
                    logcatPidList.remove(logcatPid)
                } else {
                    XLog.e("Stop record logcat failed. error:${result.stderr()}")
                }
            } catch (e: Exception) {
                XLog.e("Stop record logcat failed. error:${e.message}")
            }
            checkAndDeleteOldLogcatLog(Constant.LOGCAT_LOG_PROJECT, RECORD_FILE_MAX_COUNT)
        }
    }

    /**
     * 开始将指定时间日志记录到文件，并实时记录后续logcat日志
     * @param filePath 日志文件路径
     * @param startRecordTime 日志开始时间, 格式 "月-日 小时:分钟:秒.毫秒", 如:12-09 09:47:31.198
     */
    fun startRecordLogcat(
        filePath: String,
        startRecordTime: String?,
        logFileSizeKB: Int,
        logFileCount: Int,
        callback: (logcatPid: String?) -> Unit
    ) {
        ThreadUtils.getCachedPool().execute {
            val shell = Shell("sh")
            try {
                // 启动新进程实时抓取logcat日志文件中
                // 没有传入时间时，记录所有logcat日志，否则从传入的时间开始记录logcat日志
                val timeFilter = if (startRecordTime != null) {
                    // 转换日期格式，确保日期格式正确
                    filterLogDateFormat.parse(startRecordTime)
                    "-T '$startRecordTime'"
                } else ""

                // -r:每个日志多少kb, 需要配合-f使用
                // -n:日志文件数量， 超过数量之后循环覆盖
                // -e:过滤日志内容
                val command =
                    "logcat -r $logFileSizeKB -n $logFileCount $timeFilter -e $LOG_FILTER -f $filePath &"
                var result = shell.run(command)
                if (result.isSuccess) {
                    // 打印最后运行的进程id， 即上面打印日志进程
                    result = shell.run("echo $!")
                    if (result.isSuccess) {
                        val pid = result.output()
                        // 将启动的日志记录进程添加到进程id列表中， 便于后续停止全部日志进程时使用
                        logcatPidList.add(pid)
                        XLog.i("Start record logcat log success, pid:$pid ")
                        callback(pid)
                    } else {
                        XLog.e("Start record logcat log failed, error:${result.stderr()} ")
                        callback(null)
                    }
                } else {
                    XLog.e("Start record logcat failed. error:${result.stderr()}")
                    callback(null)
                }
                shell.shutdown()
            } catch (e: Exception) {
                XLog.e("Start record logcat failed. error:${e.message}")
                callback(null)
                shell.shutdown()
            }
        }
    }

    /**
     * 停止所有日志记录
     */
    @JvmStatic
    fun stopAllRecord() {
        ThreadUtils.getCachedPool().execute {
            logcatPidList.forEach {
                stopRecordLogcat(it)
            }
            // 删除多余文件
            checkAndDeleteOldLogcatLog(Constant.LOGCAT_LOG_PROJECT, RECORD_FILE_MAX_COUNT)
        }
    }

    /**
     * 检查并删除旧日志
     * 日志目录只保存500个文件
     */
    fun checkAndDeleteOldLogcatLog(dirPath:String , maxFileCount:Int) {
        val logcatLogDir = File(dirPath)
        if (!logcatLogDir.exists() || !logcatLogDir.isDirectory) {
            return
        }
        val fileList = logcatLogDir.listFiles()
        if (fileList == null || fileList.size <= maxFileCount) {
            return
        }
        // 根据文件时间排序
        fileList.sortBy { it.lastModified() }
        fileList.forEach {
            XLog.e("Log file:${it.name}")
        }
        // 需要删除的文件数， 只保留maxFileCount个
        val deleteCount = fileList.size - maxFileCount
        for (index in 0 until deleteCount) {
            fileList[index].delete()
        }
    }
}